/*
 * Copyright (c) 2023-2025 110476592@qq.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file screenshot_panel.cpp
 * @brief 截图工具 
 * @author 110476592@qq.com 
 * @note   承接Qt项目外包定制开发。长期接收C++项目实训学徒,联系QQ：110476592。
 */

 
#include "screenshot_panel.h"
#include <QApplication>
#include <QScreen>
#include <QPainter>
#include "screenshot_toolbar.h"
#include <QClipboard>
#include <QFileDialog>
#include <common/global.h>
#include <common/draw_context.h>

#define HANDLE_SIZE 12 //操控点的大小

ScreenshotPanel::ScreenshotPanel(const QRect &rect, QWidget *parent)
    : DrawPanel(rect,parent)
{
    setWindowFlags(Qt::Widget
                   | Qt::FramelessWindowHint //去掉边框
                   | Qt::Tool //任务栏不显示图标
                   |Qt::WindowSystemMenuHint // 隐藏系统菜单
                   | Qt::WindowStaysOnTopHint // 显示在最上层
                   );

    //获取屏幕的大小，并且将本窗口设置为屏幕大小
     QScreen * sc = QApplication::primaryScreen();
     QPixmap sourcePixmap = sc->grabWindow(0); //获取屏幕的图片
     auto devicePixelRatio = sourcePixmap.devicePixelRatio();


     /*此属性保持屏幕的物理像素与设备无关像素之间的比率
      返回屏幕的物理像素与设备无关像素之间的比率。
      普通显示器上的常见值为1.0，“视网膜”显示器上的常用值为2.0。
      更高的值也是可能的。
      */
     sourcePixmap.setDevicePixelRatio(
         QGuiApplication::primaryScreen()->devicePixelRatio());


     m_pixmap = sourcePixmap.copy(QRect(rect.left() * devicePixelRatio,
                                rect.top() * devicePixelRatio,
                                rect.width() * devicePixelRatio,
                                rect.height() * devicePixelRatio));

    m_originPixmap = m_pixmap;

    setGeometry(rect);
    m_selection = QRect();

    //初始化选区边上的点和颜色
    m_uiColor= QColor(255,0,0);
    m_handles.reserve(10);//设置够容量，避免重新分配内存
    for(int i =0; i < 8 ; ++i){
        m_handles.push_back(QRect(0, 0, HANDLE_SIZE, HANDLE_SIZE));
    }

    setMouseTracking(true);// 设置窗体追踪鼠标,窗体默认设置为不追踪。
                                // 默认情况，只有鼠标按键按下后
                                //鼠标移动时mousePressEvent才会被调用。
                                //但是如果mousetraking设置为true，只需要移动鼠标就会调用
    m_workMode = WorkMode::AREA_SELECT;
    DrawContext::setPainterType(PainterType::PENCIL);
}

ScreenshotPanel::~ScreenshotPanel()
{

}

void ScreenshotPanel::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);//消除未用的参数的编译警告
    //将桌面的图像绘制到桌面上。
    QPainter painter(this);
    painter.drawPixmap(0,0,m_pixmap);

    drawHandler(painter);


}

void ScreenshotPanel::drawHandler(QPainter &painter)
{
    painter.save();
    //确保选区是一个规范化的矩形；即宽度和高度都为非负数的矩形。
    //如果宽度() < 0，则函数会交换左、右角点，如果高度() < 0，则函数会交换上、下角点。
    //同时，角点从非包含变为包含。所以宽度和高度应该-1,
    QRect r = m_selection.normalized().adjusted(0, 0, -1, -1);

    QColor overlayColor(0, 0, 0, m_opacity);
    painter.setBrush(overlayColor);

    //创建一个和当前窗口一样大的区域。
    QRegion grey(rect());
    grey = grey.subtracted(r);//从区域中减掉选中的rect,

        painter.setClipRegion(grey);//设置需要绘制的区域,不会只选择区。
    // 画大一个像素，提高判断边框的准确率
    painter.drawRect(-1, -1, rect().width() + 1, rect().height() + 1);

    //因为选区已经设置为不在绘画的范围内，所以handles只会有一半显示
    if (!m_selection.isNull()) {
        painter.setPen(m_uiColor);
        painter.setBrush(Qt::NoBrush);
        painter.drawRect(r);

        //绘图模式下不显示控制点
        if(m_workMode == WorkMode::AREA_SELECT){
            // 绘制选区边上的点
            painter.setRenderHint(QPainter::Antialiasing);
            painter.setBrush(m_uiColor);
            for (int i =0;i < m_handles.size();++i)
            {
                painter.drawRoundedRect(m_handles[i],HANDLE_SIZE/2,HANDLE_SIZE/2);
            }
        }
    }
    painter.restore();
}

void ScreenshotPanel::updateHandlerPosition()
{
    //先转换成规范矩形再判断，不然反选时会为空
    QRect rc = m_selection.normalized();
    if(rc.isEmpty()){
        return ;
    }

    rc = rc.adjusted(0, 0, -1, -1);
    int width = HANDLE_SIZE / 2;
    m_handles[0].moveTo(rc.x()- width ,rc.y()- width);
    m_handles[1].moveTo(rc.x() + rc.width()/2 - width ,rc.y()- width);
    m_handles[2].moveTo(rc.right()- width,rc.y() - width);
    m_handles[3].moveTo(rc.right()- width,rc.y()+rc.height()/2 - width);
    m_handles[4].moveTo(rc.right()- width, rc.y() + rc.height() - width);
    m_handles[5].moveTo(rc.x()+rc.width()/2 - width,rc.y() + rc.height() - width);
    m_handles[6].moveTo(rc.x() - width ,rc.y() + rc.height() - width);
    m_handles[7].moveTo(rc.x()- width,rc.y() + rc.height()/2 -width);
}

void ScreenshotPanel::updateSelectCursor() {
    if (m_selection.isEmpty()) {
        setCursor(Qt::ArrowCursor);
        return ;
    }

    QPoint pt = QCursor::pos();
    QPoint pt2 = mapFromGlobal(pt);

    if(!m_leftPressed){
        if(m_handles[0].contains(pt2)){
            setCursor(Qt::SizeFDiagCursor);
            m_resizeIndex = 0 ;
        }else if(m_handles[1].contains(pt2)){
            setCursor(Qt::SizeVerCursor);
            m_resizeIndex = 1;
        }else if(m_handles[2].contains(pt2)){
            setCursor(Qt::SizeBDiagCursor);
            m_resizeIndex = 2;
        }else if(m_handles[3].contains(pt2)){
            setCursor(Qt::SizeHorCursor);
            m_resizeIndex = 3;
        }else if(m_handles[4].contains(pt2)){
            setCursor(Qt::SizeFDiagCursor);
            m_resizeIndex = 4;
        }else if(m_handles[5].contains(pt2)){
            setCursor(Qt::SizeVerCursor);
            m_resizeIndex = 5;
        }else  if(m_handles[6].contains(pt2)){
            setCursor(Qt::SizeBDiagCursor);
            m_resizeIndex = 6;
        }else  if(m_handles[7].contains(pt2)){
            setCursor(Qt::SizeHorCursor);
            m_resizeIndex = 7;
        }else{
            setCursor(Qt::ArrowCursor);
            m_resizeIndex = -1;
        }
    }

}

void ScreenshotPanel::resizeSelection(const QPoint &offset)
{
    QRect r = m_selectionBeforeChange;
    switch (m_resizeIndex) {
    case 0:
        r.setLeft(r.left() + offset.x());
        r.setTop(r.top() + offset.y());
        break;
    case 1:
        r.setTop(r.top() + offset.y());
        break;
    case 2:
        r.setRight(r.right() + offset.x());
        r.setTop(r.top() + offset.y());
        break;
    case 3:
        r.setRight(r.right() + offset.x());
        break;
    case 4:
        r.setRight(r.right() + offset.x());
        r.setBottom(r.bottom() + offset.y());
        break;
    case 5:
        r.setBottom(r.bottom() + offset.y());
        break;
    case 6:
        r.setLeft(r.left() + offset.x());
        r.setBottom(r.bottom() + offset.y());
        break;
    case 7:
        r.setLeft(r.left() + offset.x());
        break;
    default:
        return;
    }

    m_selection = r.normalized();
}


void ScreenshotPanel::createToolBar()
{
    m_floatToolBar = new ScreenshotToolBar(this);
    m_floatToolBar->setVisible(false);
}

bool ScreenshotPanel::handleCustomeCommand(ToolButtonEx *btn){

    switch (btn->commandType()) {
    case CommandType::CMD_CLOSE: {
        close();
        return true;
    }

    default:
        break;
    }
    return false;
    return false;
}

void ScreenshotPanel::updateToolBarPosition()
{
    QRect rc = m_selection.normalized();

    if(rc.isNull()){
        return ;
    }
    m_floatToolBar->updatePosition(rect(),rc);
    m_floatToolBar->setVisible(true);
}

void ScreenshotPanel::mousePressEvent(QMouseEvent *event)
{
    //绘图模式下不处理选区
    if(m_workMode == WorkMode::AREA_SELECT)
    {
        startSelectArea(event);
    }else if(m_workMode == WorkMode::AREA_DRAW)
    {
        DrawPanel::mousePressEvent(event);
    }

}

void ScreenshotPanel::startSelectArea(QMouseEvent *event)
{

    if(event->button() == Qt::RightButton ){
        m_leftPressed = false;
        m_selection = QRect(0,0,0,0);
        m_selectNew = false;
        m_floatToolBar->setVisible(false);
        update();
    }else if(event->button() == Qt::LeftButton ){

         QPoint pt = event->pos();
         //在选择模式下，如果不在选区内点击不处理
         QRect outSelection = m_selection;
         //距离边界太远就不让点了。
         outSelection.adjust(1,1,1,1);
         if(!m_selection.isEmpty() && !outSelection.contains(pt)){
            return ;
         }
         m_leftPressed = true;


         if(m_resizeIndex >= 0){ //表示整在调整选区尺寸
            m_startPoint = pt;
            m_selectionBeforeChange = m_selection;
        }else{
        	// 如果当前选区不为空，并且鼠标位置在选区内，表示执行移动选区。
        	if(m_selection.contains(pt)){
            	m_startPoint = pt;
                m_offset = pt - m_selection.topLeft();
        	}else{
            	//如果当前选区为空，表示新选区，获取当前鼠标相对于窗口的坐标
            	m_selection.setTopLeft(pt);
                m_offset = QPoint(0,0);
                m_selectNew = true;
            }
        }
    }
}

void ScreenshotPanel::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event);//消除未用的参数的编译警告
   m_leftPressed = false;

    //绘图模式下不处理选区
    if(m_workMode == WorkMode::AREA_SELECT)
    {
         selectAreaFinished(event);
    }else if(m_workMode == WorkMode::AREA_DRAW)
    {
        DrawPanel::mouseReleaseEvent(event);
    }
}

void ScreenshotPanel::selectAreaFinished(QMouseEvent *event)
{
    Q_UNUSED(event);
   m_startPoint = QPoint(0,0);
   if(m_selectNew){
        updateToolBarPosition();
        m_selectNew = false;
   }
}

void ScreenshotPanel::mouseMoveEvent(QMouseEvent *event)
{
   if(m_workMode == WorkMode::AREA_SELECT)
   {
        moveSelectedArea(event);
        update();
   }else if(m_workMode == WorkMode::AREA_DRAW)
   {
        DrawPanel::mouseMoveEvent(event);
   }


}


void ScreenshotPanel::moveSelectedArea(QMouseEvent *event){
    if(m_leftPressed)
    {
        //如果起点为空，表示在执行选择区域，否则表示移动选区
        if(m_startPoint.isNull()){
            m_selection.setBottomRight(event->pos());
        }else{
            if(m_resizeIndex >= 0){ //表示整在调整选区尺寸
                //计算变化的位置
                QPoint offset = event->pos() - m_startPoint;
                resizeSelection(offset);
            }else{
                // 计算移动的位置
                QPoint p =  event->pos() - m_offset;
                m_selection.moveTo(p);
                //确保选区不会超出窗口的边界
                adjustSelection();
            }
            updateToolBarPosition();

        }

        updateHandlerPosition();
    }

    updateSelectCursor();
}

void ScreenshotPanel::adjustSelection()
{
    QRect r = rect();

    if (!r.contains(QPoint(r.center().x(), m_selection.top()))) {
        m_selection.setTop(r.top());
    }

    if (!r.contains(QPoint(m_selection.left(), r.center().y()))) {
        m_selection.setLeft(r.left());
    }

    if (!r.contains(QPoint(m_selection.right(), r.center().y()))) {
        m_selection.setRight(r.right());
    }

    if (!r.contains(QPoint(r.center().x(), m_selection.bottom()))) {
        m_selection.setBottom(r.bottom());
    }
}

